// Main application to display ray tracing output onto OpenGL Window
/////////////////////////////////////////////////////////////////////

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#elif defined(_WIN32)
#include <windows.h>
#include <GL/glew.h>
#include <GL/glut.h>
#else
#include <GL/glew.h>
#include <GL/glut.h>
#endif

#include <cstdlib>
#include <cstdio>

// Ray Tracing Includes
#include "Engine.h"
#include "Vector3.h"
#include "Vector4.h"


char* window_title = "Ray Tracing";
int main_window = 1;
int position_x = 100;
int position_y = 50;
int width = 400;
int height = 300;

static GLuint texName;

// Ray Tracing Variables
RayTracer::CEngine* rayTracer = NULL;

// Function Declarations
/////////////////////////

/* OpenGL Callbacks */
void idle();
void keyboard(unsigned char key, int x, int y);
void reshape(int x, int y);
void display();

// Rendering Functions
void initGL();

int main(int argc, char* argv[])
{
	//
	// create the glut window
	//
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH);
	glutInitWindowSize(width, height);
	glutInitWindowPosition(position_x, position_y);
	main_window = glutCreateWindow(window_title);

	// Init Glew
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	}
	fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));

	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutReshapeFunc(reshape);
	glutIdleFunc(idle);

	// Init Ray Tracer
	RayTracer::CVector3 cameraPosition(0, 0, -5);
	RayTracer::CVector4 backgroundColor(0.2f, 0.2f, 0.2f, 1.0f);
	rayTracer = new RayTracer::CEngine(cameraPosition, backgroundColor);
	rayTracer->getScene()->InitScene();
	rayTracer->setTarget(width, height);
	rayTracer->InitRender();

	initGL();

	// give control over to glut
	glutMainLoop();

	delete rayTracer;

	return EXIT_SUCCESS;
}

// Function Definitions
////////////////////////

void idle(void)
{
	if (glutGetWindow() != main_window)
		glutSetWindow(main_window);
}


void keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		// quit
	case 27: 
	case 'q':
	case 'Q':
		exit(EXIT_SUCCESS);
		break;
	}
}

// the window has changed shapes, fix ourselves up
void reshape(int x, int y)
{
	width = x;
	height = y;
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, 0, height, -100, 10000);
	glMatrixMode(GL_MODELVIEW);
	glutPostRedisplay();
}

void display()
{
	glMatrixMode(GL_MODELVIEW);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, texName);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, rayTracer->getBuffer());
	
	glBegin(GL_QUADS);
	glTexCoord2f(0, 1);	glVertex2f(0.f, 0.f);
	glTexCoord2f(1, 1);	glVertex2f(width, 0.f);
	glTexCoord2f(1, 0);	glVertex2f(width, height);
	glTexCoord2f(0, 0);	glVertex2f(0.f, height);
	glEnd();
	
	glFlush();

	glDisable(GL_TEXTURE_2D);

	if (!rayTracer->Render())
		glutPostRedisplay();
}

void initGL()
{
	//Clear background to black 
	glClearColor(0, 0, 0, 0);
	glShadeModel(GL_FLAT);
	glEnable(GL_DEPTH_TEST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glGenTextures(1, &texName);
	glBindTexture(GL_TEXTURE_2D, texName);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}